#
# Handling of portal news items.
#

from xmlrpclib import ServerProxy,Fault,ProtocolError
from Exception_ import FgsException

class Category(object):
    def __init__(self, manager, **kwargs):
        self.manager = manager
        for k,v in kwargs.items():
            if k.startswith("category_"):
                k = k[9:]
            self.__dict__[k] = v

    def __getattr__(self, name):
        if name == "logo_path":
            return self._get_logo_path()
        else:
            raise AttributeError

    def _get_logo_path(self):
        if self.logo_hash == '':
            return False

        # Ask the cache if it has the right logo
        logo = self.manager.cache.retrieve_file_path(self.logo_hash)
        if logo:
            return logo

        # If the cache doesn't have it, retrieve it from the portal.
        try:
            logo_data = self.manager.proxy.user.getgamelogo(self.logo_hash)
        except Fault,f:
            raise FgsException("XmlRPC call faulted: %s" % f.faultString)
        except ProtocolError, p:
            raise FgsException("Transport error during XmlRpc call on %s: %s" % (p.errmsg, p.url))

        from base64 import decodestring
        logo_data = decodestring(logo_data['logo'])

        if self.manager.cache.cache_enabled():
            # Store the data in cache and return it
            self.manager.cache.store_file(logo_data, self.logo_hash)
            return self.manager.cache.retrieve_file_path(self.logo_hash)
        else:
            # Fallback when no cache is available.
            from os.path import join
            from os import getcwd
            file_path = join(getcwd(), self.logo_hash)
            f = file(file_path, 'w')
            f.write(logo_data)
            f.close()
            return file_path


class NewsItem(object):
    def __init__(self, manager, **kwargs):
        self.manager = manager
        for k,v in kwargs.items():
            if k.startswith("news_"):
                k = k[5:]
                self.__dict__[k] = v

        # Replace the litteral category ID with a nice reference to
        # the category object
        for cat in manager.categories.values():
            if cat.id == self.category:
                self.category = cat
                break

        # Store the category logo path as well.

        # ### TODO: This is crass and GFgs specific. Find a way to
        # ### decouple.
        self.logo_path = self.category.logo_path

    def __getattr__(self, name):
        if name in self.__dict__:
            return self.__dict__[name]
        if name == 'data':
            self.__dict__['data'] = self._get_data()
            return self.__dict__['data']
        else:
            raise AttributeError

    def clear(self):
        del self.__dict__['data']

    def _get_data(self):
        try:
            news_data = self.manager.proxy.news.getnewsitem(self.id)
        except Fault, f:
            raise FgsException("XmlRpc call faulted: %s" % f.faultString)
        except ProtocolError, p:
            raise FgsException("Transport error during XmlRpc call on %s: %s" % (p.errmsg, p.url))

        # This pattern matches a character entity reference (a decimal numeric
        # references, a hexadecimal numeric reference, or a named reference).
        import re
	from htmlentitydefs import name2codepoint

        charrefpat = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?')

        def htmldecode(text):
            """Decode HTML entities in the given text."""
            if type(text) is unicode:
                uchr = unichr
            else:
                uchr = lambda value: value > 127 and unichr(value) or chr(value)
            
	    def entitydecode(match, uchr=uchr):
                entity = match.group(1)
                if entity.startswith('#x'):
                    return uchr(int(entity[2:], 16))
                elif entity.startswith('#'):
                    return uchr(int(entity[1:]))
                elif entity in name2codepoint:
                    return uchr(name2codepoint[entity])
                else:
                    return match.group(0)
            return charrefpat.sub(entitydecode, text)

	text = htmldecode(news_data['text'])
        return text


class News(object):
    def __init__(self, portal):
        self.portal = portal
        self.cache = portal._cache
        self.proxy = portal._proxy
        self._get_news()

    def _get_news(self):
        self.categories = self._get_categories()
        self.items = self._get_news_items()

    def _get_categories(self):
        categories = []
        try:
            cat_list = self.proxy.news.getcategorylist()
        except Fault, f:
            raise FgsException("XmlRpc call faulted: %s" % f.faultString)
        except ProtocolError, p:
            raise FgsException("Transport error during XmlRpc call on %s: %s" % (p.errmsg, p.url))

        for cat in cat_list:
            categories.append(Category(self, **cat))

        return dict([(x,categories[x]) for x in range(len(categories))])

    def _get_news_items(self):
        items = []
        try:
            item_list = self.proxy.news.getnewslist(15)
        except Fault, f:
            raise FgsException("XmlRpc call faulted: %s" % f.faultString)
        except ProtocolError, p:
            raise FgsException("Transport error during XmlRpc call on %s: %s" % (p.errmsg, p.url))

        for item in item_list:
            items.append(NewsItem(self, **item))

        return dict([(x,items[x]) for x in range(len(items))])

